import pytz
from zope import interface, component
from zope.interface.common.idatetime import ITZInfo
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.formlib.interfaces import IFormField
from zope.app.form.interfaces import MissingInputError
from zope.formlib import form, interfaces
from zope.exceptions.interfaces import UserError

from interfaces import IDataAdapter, IDBFormlibContent
from utils import ENTRY_FIELD_PREFIX

getUserId = None

try:
    from OFS.Folder import Folder
    getUserId = lambda self: self.request.AUTHENTICATED_USER.id
except:
    getUserId = lambda self: self.request.principal.id

class DataAdapter(object):
    """ See interfaces.py    
    """
    interface.implements(IDataAdapter)
    component.adapts(interfaces.IForm)
    
    def __init__(self, view):
        self.view = view
        self.context = view.context
        self.request = view.request
        self.crud_id = view.crud_id
        self._entry_fields = view._entry_fields
        self.form_reset = view.form_reset
        
        self.form_fields = []
        if view.form_fields:
            self.form_fields = view.form_fields.__FormFields_byname__.keys()
        if self._entry_fields:
            self.form_fields.extend(self._entry_fields.__FormFields_byname__.keys())
        self._data = {}
        
    def get_data(self, ignore_request=False):
        """ Retrieves form data
            If cache is not used then it has to:
            1. retrieve values of entry fields from request
            2. call _get_data function with entry_fields values
               as parameters
            3. return data
        """
        use_cached = self._use_cached(self.view.actions)
        
        in_params = {}
        if not use_cached:
            in_params = self._get_entry_values(ignore_request)
            
        return self._get_data(use_cached, in_params)
        
        
    def _get_data(self, use_cached=True, in_params={}):
        if use_cached and self._data:
            return self._data
        
        if not in_params and self._entry_fields and not use_cached:
            raise UserError('No entry parameters in request!')
        
        self._data = self.context.get_values(crud_id=self.crud_id,
                                             use_cache=use_cached,
                                             ckey=getUserId(self),
                                             in_params=in_params)
        return self._data
        
    def apply_data(self, form_fields, data):
        """ Saves data (calls user defined function), 
            but first checks if there were changes
        """
        # get cached data
        cdata = self._get_data()

        changed = False
        for form_field in form_fields:
            field = form_field.field
            name = form_field.__name__
            newvalue = data.get(name, form_field) # using form_field as marker
            
            if (newvalue is not form_field) and (cdata.get(name, None) != newvalue):
                changed = True
        errors = []
        result = None
        if changed:
            result, errors = self.set_data(data)

        return changed, result, errors
        
    def set_data(self, data):
        """ Saves data (calls user defined function)
        """
        result, errors = self.context.set_values(crud_id=self.crud_id,
                                               ckey=getUserId(self),
                                               data=data)
        return result, errors
        
    def _use_cached(self, actions):
        """ Determine whether form data should be taken from cached
            result. This is possible if request has attribute
            same as value of _use_cache_id
            or if there are actions submitted
        """        
        _use_cache_id = self.context.get_use_cache_id()
        use_cache = self.request.form.has_key(_use_cache_id)
        if self.form_reset:
            return False
        if not use_cache:
            for action in actions:
                # this doesn't check if there is
                # action 'condition' fullfilled because
                # it may be not possible at this moment, eg.
                # there are no form.widgets yet
                if action.__name__ in self.request.form:                    
                    use_cache = True
                    break
        return use_cache
    
    def _get_entry_values(self, ignore_request=False):
        """ get entry values from the request (entry values)
        """
        entry_widgets = []
        data={}
        if self._entry_fields:
            entry_widgets = form.setUpDataWidgets(\
                                    form_fields=self._entry_fields, 
                                    form_prefix=ENTRY_FIELD_PREFIX, 
                                    context=self.context, 
                                    request=self.request, 
                                    data=data, 
                                    ignore_request=ignore_request)
        
            form_prefix = ENTRY_FIELD_PREFIX + '.'

            for input, widget in entry_widgets.__iter_input_and_widget__():
                name = form._widgetKey(widget, form_prefix)

                try:
                    data[name] = widget.getInputValue()
                except MissingInputError, err:
                    if not widget.required:
                        data[name] = None
                    else:
                        raise UserError(err)
                except Exception, error:
                    raise UserError(error)
        return data
        
    def __getattr__(self, name):
        # only works with cached data
        data = self._get_data()
        if data.has_key(name):
            return data[name]
        elif name in self.form_fields:
            return None
        raise AttributeError, name
        
    def __setattr__(self, name, value):
        if not name=='_data' and \
          self.__dict__.has_key('_data') \
          and name in self.__dict__['_data'].keys():
            self._data[name] = value  
        else:
            object.__setattr__(self, name, value)